Skip to content

feat: enable translation or customization of the constants "<NULL>", "TRUE", "FALSE" and "<empty string>"#40919

Open
nicob3y wants to merge 1 commit into
apache:masterfrom
nicob3y:feat/null-true-false-translatable
Open

feat: enable translation or customization of the constants "<NULL>", "TRUE", "FALSE" and "<empty string>"#40919
nicob3y wants to merge 1 commit into
apache:masterfrom
nicob3y:feat/null-true-false-translatable

Conversation

@nicob3y

@nicob3y nicob3y commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

This PR aims to easily allow translation or customization of the constants <NULL>, TRUE, FALSE, and <empty string>.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Here is an example of customizing <NULL> into French, before and after:

image image

TESTING INSTRUCTIONS

To test this change, you must first translate the strings in question and observe the screens where they may be displayed.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@dosubot dosubot Bot added the i18n:general Related to translations label Jun 9, 2026
@netlify

netlify Bot commented Jun 9, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 8393ff8
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a291b79d0b05900081429dc
😎 Deploy Preview https://deploy-preview-40919--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.15%. Comparing base (0a1e51f) to head (16d83b1).
⚠️ Report is 6 commits behind head on master.

Files with missing lines Patch % Lines
...lugin-chart-echarts/src/Sunburst/transformProps.ts 0.00% 3 Missing ⚠️
...ugin-chart-echarts/src/Waterfall/transformProps.ts 0.00% 2 Missing ⚠️
...lugin-chart-echarts/src/Treemap/EchartsTreemap.tsx 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #40919      +/-   ##
==========================================
- Coverage   64.17%   64.15%   -0.03%     
==========================================
  Files        2654     2655       +1     
  Lines      143763   143827      +64     
  Branches    33161    33166       +5     
==========================================
+ Hits        92260    92272      +12     
- Misses      49887    49934      +47     
- Partials     1616     1621       +5     
Flag Coverage Δ
hive 39.46% <100.00%> (-0.02%) ⬇️
javascript 67.98% <77.77%> (-0.01%) ⬇️
mysql 58.18% <100.00%> (-0.03%) ⬇️
postgres 58.25% <100.00%> (-0.04%) ⬇️
presto 41.05% <100.00%> (-0.02%) ⬇️
python 59.72% <100.00%> (-0.04%) ⬇️
sqlite 57.87% <100.00%> (-0.03%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bito-code-review bito-code-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #f1b1a5

Actionable Suggestions - 1
  • superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx - 1
Additional Suggestions - 3
  • superset-frontend/plugins/plugin-chart-echarts/src/constants.ts - 1
    • Missing Python backend constants · Line 32-35
      The diff adds TRUE_STRING and FALSE_STRING with a comment directing developers to also update constants.py, but these constants are missing from the Python backend file. The existing NULL_STRING and EMPTY_STRING are already mirrored in constants.py (lines 30-31), but TRUE_STRING and FALSE_STRING are not. This creates an inconsistency that could cause issues if backend code needs to reference these values.
  • superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts - 1
    • Dead code removal needed · Line 72-74
      The diff correctly changes `NULL_STRING` to `NULL_STRING()` (calling the function). However, the entire code block (lines 72-74) is dead code — after `return ''` at line 68, `series` is guaranteed truthy, making `if (!series)` at line 72 unreachable. The proper fix is to delete lines 72-74 entirely rather than fixing code that can never execute.
  • superset-frontend/src/components/FilterableTable/useCellContentParser.ts - 1
    • Inconsistent null label text · Line 25-25
      The `NULL_STRING` now returns `'NULL'` but `plugin-chart-echarts/src/constants.ts:33` uses `''` with angle brackets. Users may see inconsistent null representations depending on the displaying component.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts - 1
Review Details
  • Files reviewed - 18 · Commit Range: a052f97..a052f97
    • superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Treemap/EchartsTreemap.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/constants.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts
    • superset-frontend/src/components/FilterableTable/useCellContentParser.test.ts
    • superset-frontend/src/components/FilterableTable/useCellContentParser.ts
    • superset-frontend/src/components/FilterableTable/utils.tsx
    • superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx
    • superset-frontend/src/explore/exploreUtils/getSimpleSQLExpression.test.ts
    • superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx
    • superset-frontend/src/filters/utils.test.ts
    • superset-frontend/src/filters/utils.ts
    • superset-frontend/src/utils/common.test.tsx
    • superset-frontend/src/utils/common.ts
    • superset/constants.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

import CollectionControl from '.';

jest.mock('@superset-ui/chart-controls', () => ({
...jest.requireActual('@superset-ui/chart-controls'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mock targets non-existent export

The mock targets InfoTooltip from @superset-ui/chart-controls, but this component is not exported from that module. The actual source imports InfoTooltip from @superset-ui/core/components (see ControlHeader.tsx:22). This mock will have no effect and may cause test failures.

Code suggestion
Check the AI-generated fix before applying
 --- superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx
 +++ superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx
 @@ -19,7 +19,7 @@ import { render, screen, userEvent } from 'spec/helpers/testing-library';
  import CollectionControl from '.';
 
  jest.mock('@superset-ui/chart-controls', () => ({
 -  ...jest.requireActual('@superset-ui/chart-controls'),
 +  ...jest.requireActual('@superset-ui/chart-controls'),
    InfoTooltip: (props: any) => (
      <button
        onClick={props.onClick}
        type="button"
        data-icon={props.icon}
        data-tooltip={props.tooltip}
      >
        {props.label}
      </button>
    ),
  }));
 
  jest.mock('@superset-ui/core/components', () => ({
    ...jest.requireActual('@superset-ui/core/components'),
    InfoTooltip: (props: any) => (
      <button
        onClick={props.onClick}
        type="button"
        data-icon={props.icon}
        data-tooltip={props.tooltip}
      >
        {props.label}
      </button>
    ),
  }));

Code Review Run #f1b1a5


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

@nicob3y nicob3y force-pushed the feat/null-true-false-translatable branch from 2cc6bab to 8393ff8 Compare June 10, 2026 08:08

@bito-code-review bito-code-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #91c55e

Actionable Suggestions - 2
  • superset-frontend/plugins/plugin-chart-echarts/src/constants.ts - 1
  • superset/constants.py - 1
    • Use lazy_gettext for module constants · Line 26-26
Additional Suggestions - 1
  • superset-frontend/src/utils/common.ts - 1
    • Overly specific type annotation · Line 45-45
      The type 'ReturnType' is overly specific. Since all four constants are functions returning strings, using 'string' would be simpler and more maintainable.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts - 1
Review Details
  • Files reviewed - 18 · Commit Range: 58077d8..8393ff8
    • superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Treemap/EchartsTreemap.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/constants.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts
    • superset-frontend/src/components/FilterableTable/useCellContentParser.test.ts
    • superset-frontend/src/components/FilterableTable/useCellContentParser.ts
    • superset-frontend/src/components/FilterableTable/utils.tsx
    • superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx
    • superset-frontend/src/explore/exploreUtils/getSimpleSQLExpression.test.ts
    • superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx
    • superset-frontend/src/filters/utils.test.ts
    • superset-frontend/src/filters/utils.ts
    • superset-frontend/src/utils/common.test.tsx
    • superset-frontend/src/utils/common.ts
    • superset/constants.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo


// eslint-disable-next-line import/prefer-default-export
export const NULL_STRING = '<NULL>';
// ATTENTION: If you change any constants, make sure to also change constants.py

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation Inconsistency

The comment at line 30 states 'If you change any constants, make sure to also change constants.py', but the new TRUE_STRING and FALSE_STRING constants lack Python counterparts. Either update the comment to scope it to NULL_STRING/EMPTY_STRING, or add the new constants to Python to honor the stated contract.

Code Review Run #91c55e


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Comment thread superset/constants.py

from superset.utils.backports import StrEnum

from flask_babel import gettext as __

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use lazy_gettext for module constants

Using gettext for module-level constants can fail when the module is imported before a request context is established. The codebase consistently uses lazy_gettext for constants (see initialization/__init__.py:33 comment: 'using lazy_gettext since initialization happens prior to the request scope'). This pattern is verified across 20+ files importing lazy_gettext for similar use cases.

Code suggestion
Check the AI-generated fix before applying
 --- a/superset/constants.py
 +++ b/superset/constants.py
 @@ -23,7 +23,7 @@
 
  from superset.utils.backports import StrEnum
 
 -from flask_babel import gettext as __
 +from flask_babel import lazy_gettext as __
 
  DEFAULT_USER_AGENT = "Apache Superset"
 

Code Review Run #91c55e


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

@nicob3y nicob3y force-pushed the feat/null-true-false-translatable branch from 8393ff8 to 16d83b1 Compare June 10, 2026 17:57
Comment on lines 87 to 93
export function optionValue(
opt: OptionValue,
): OptionValue | typeof NULL_STRING {
): OptionValue | ReturnType<typeof NULL_STRING> {
if (opt === null) {
return NULL_STRING;
return NULL_STRING();
}
return opt;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Converting null into the localized display token for the option value collapses two distinct inputs (null and a real string equal to that token) into the same value, which causes ambiguous selections and incorrect round-tripping. Keep the underlying option value as null and only localize the label. [logic error]

Severity Level: Major ⚠️
- ⚠️ Option helpers collapse NULL and sentinel string values.
- ⚠️ Filters may treat real sentinel text as SQL NULL.
Steps of Reproduction ✅
1. Open `superset-frontend/src/utils/common.ts` and note `optionValue` at lines 87-93 and
`optionFromValue` at lines 96-98: `optionValue` returns `NULL_STRING()` when `opt ===
null`, and `optionFromValue` uses this as the `value` field of `OptionItem`.

2. In a TypeScript runtime (or unit test), import `optionFromValue` and `NULL_STRING` from
`superset-frontend/src/utils/common.ts` (re-exported at lines 27-35) and evaluate:

   ```ts

   const a = optionFromValue(null); // uses optionValue/optionLabel

   const b = optionFromValue(NULL_STRING()); // pass a real string equal to the null token
  1. Observe that a.value === b.value because for null input optionValue returns
    NULL_STRING() (line 90-91), while for NULL_STRING() input optionValue returns the
    same string unchanged (line 93), even though the original inputs (null vs actual string)
    are different.

  2. Any consumer using OptionItem.value (type OptionValue | ReturnType<typeof NULL_STRING> at line 45) for identity, selection, or to build backend filter payloads can
    no longer distinguish database rows whose actual value equals the localized null token
    from rows that are truly NULL, leading to ambiguous selections and incorrect
    round-tripping once this helper is used in production code.

</details>

[Fix in Cursor](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=efed6b1328d448b0bfef093d2a8cc056&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset) | [Fix in VSCode Claude](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=efed6b1328d448b0bfef093d2a8cc056&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)

*(Use Cmd/Ctrl + Click for best experience)*
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>

```mdx
This is a comment left during a code review.

**Path:** superset-frontend/src/utils/common.ts
**Line:** 87:93
**Comment:**
	*Logic Error: Converting `null` into the localized display token for the option `value` collapses two distinct inputs (`null` and a real string equal to that token) into the same value, which causes ambiguous selections and incorrect round-tripping. Keep the underlying option value as `null` and only localize the label.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment thread superset/constants.py
Comment on lines +24 to +31
from flask_babel import gettext as __

from superset.utils.backports import StrEnum

DEFAULT_USER_AGENT = "Apache Superset"

NULL_STRING = "<NULL>"
EMPTY_STRING = "<empty string>"
NULL_STRING = __("<NULL>")
EMPTY_STRING = __("<empty string>")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: gettext is being executed at module import time, so these translated constants are resolved once (typically in the default locale) and then reused for all requests. That breaks per-user localization and can also desynchronize frontend/backend sentinel values across locales. Use lazy translation (or translate at usage time) so the value is resolved in the active request locale. [logic error]

Severity Level: Critical 🚨
- ⚠️ NULL and empty tokens ignore active request locale.
- ⚠️ Frontend and backend NULL sentinels diverge across locales.
Steps of Reproduction ✅
1. Open `superset/constants.py` and note at lines 24-31 that `gettext` is imported as `__`
and immediately invoked to define `NULL_STRING = __("<NULL>")` and `EMPTY_STRING =
__("<empty string>")`, so these translations are resolved once at module import time.

2. In a Flask context, demonstrate locale sensitivity by comparing:

   - `from flask_babel import gettext as __; __("<NULL>")` executed inside a
   `force_locale("fr")` block (returns French translation), versus

   - `from superset.constants import NULL_STRING; NULL_STRING` inside the same block
   (still returns the value computed when `superset.constants` was first imported,
   typically English), proving `NULL_STRING` is not per-request localized.

3. Open `superset/utils/pandas_postprocessing/pivot.py` and observe `pivot()` at lines
10-32, where the `column_fill_value` default is `NULL_STRING` (line 17-31 comment). When a
chart query uses the pivot post-processing operation (wired via
`superset/utils/pandas_postprocessing/__init__.py:17-31` and the generic post-processing
pipeline), any missing column labels are filled with this frozen constant, so users on
non-default locales see the startup-locale token instead of a value in their active
locale.

4. Open `superset/models/helpers.py` around lines 2520-2533 and note `handle_single_value`
compares incoming filter values against `NULL_STRING` and `EMPTY_STRING` (lines 2520-2523)
to turn those sentinel strings back into `None` or `""`. On the frontend, the
corresponding constants are defined in
`superset-frontend/plugins/plugin-chart-echarts/src/constants.ts:32-35` as functions
`NULL_STRING()`/`EMPTY_STRING()` that call `t('<NULL>')`/`t('<empty string>')` per current
UI locale. Because the backend constants are frozen at import time, for users whose locale
differs from the server's startup locale the frontend's localized sentinel strings may no
longer equal the backend's `NULL_STRING`/`EMPTY_STRING`, causing null/empty filters not to
be recognized and desynchronizing frontend and backend sentinel handling across locales.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/constants.py
**Line:** 24:31
**Comment:**
	*Logic Error: `gettext` is being executed at module import time, so these translated constants are resolved once (typically in the default locale) and then reused for all requests. That breaks per-user localization and can also desynchronize frontend/backend sentinel values across locales. Use lazy translation (or translate at usage time) so the value is resolved in the active request locale.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

data.forEach(datum => {
const dataName = bubbleSeries ? datum[bubbleSeries] : datum[entity];
const name = dataName ? String(dataName) : NULL_STRING;
const name = dataName ? String(dataName) : NULL_STRING();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The null fallback uses a truthy check, so valid category values like 0, false, and '' are incorrectly converted to the null label. This will merge distinct categories into the null bucket and produce incorrect legend/color grouping. Check explicitly for null/undefined instead of falsy values. [falsy zero check]

Severity Level: Major ⚠️
- ⚠️ Bubble chart merges 0-category into null legend bucket.
- ⚠️ Bubble legend mislabels falsy category values as null.
Steps of Reproduction ✅
1. In the frontend, `MainPreset` registers `EchartsBubbleChartPlugin` with key
`VizType.Bubble` at `superset-frontend/src/visualizations/presets/MainPreset.ts:198`,
which pulls `BubbleTransformProps` from `@superset-ui/plugin-chart-echarts` (re-exported
in `superset-frontend/plugins/plugin-chart-echarts/src/index.ts:11` as the default from
`./Bubble/transformProps`).

2. Prepare bubble chart form data similar to the unit test
(`superset-frontend/plugins/plugin-chart-echarts/test/Bubble/transformProps.test.ts:10-33`),
but use a dataset where the grouping column (e.g. `entity`/`customer_name`) contains a
legitimate falsy value such as `0` or `''` for one row in `queriesData[0].data` (see
structure at lines 35-58 in that test).

3. When `transformProps` runs in
`superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts:98-169`, it
iterates `data.forEach(datum => { ... })` at line 146, computes `dataName = bubbleSeries ?
datum[bubbleSeries] : datum[entity];` at line 147, and for the row where `datum[entity]`
is `0` or `''`, `dataName` is that falsy value.

4. At line 148, `const name = dataName ? String(dataName) : NULL_STRING();` treats
`0`/`''` as falsy, so `name` becomes `NULL_STRING()` instead of `'0'`/`''`, and the
legend/series name added at lines 151-169 uses the null label; this collapses the real
`0`/empty-string category into the `<NULL>` bucket, producing incorrect legend entries and
color grouping for that category.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts
**Line:** 148:148
**Comment:**
	*Falsy Zero Check: The null fallback uses a truthy check, so valid category values like `0`, `false`, and `''` are incorrectly converted to the null label. This will merge distinct categories into the null bucket and produce incorrect legend/color grouping. Check explicitly for `null`/`undefined` instead of falsy values.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

const drillByFilters: BinaryQueryObjectFilterClause[] = [];
treePath.forEach((path, i) => {
const val = path === 'null' ? NULL_STRING : path;
const val = path === 'null' ? NULL_STRING() : path;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Mapping the literal string 'null' to the null token changes semantics for real string values equal to "null", so drill filters can target the wrong records. Preserve the original value unless you can reliably detect an actual null sentinel from the source data. [incorrect condition logic]

Severity Level: Major ⚠️
- ⚠️ Treemap drill-to-detail misfilters literal 'null' category.
- ⚠️ Treemap drill-by filters mis-target literal 'null' values.
Steps of Reproduction ✅
1. `MainPreset` wires `EchartsTreemapChartPlugin` into the visualization registry with key
`VizType.Treemap` at `superset-frontend/src/visualizations/presets/MainPreset.ts:125`, and
the plugin uses `EchartsTreemap` from
`superset-frontend/plugins/plugin-chart-echarts/src/Treemap/EchartsTreemap.tsx` as its
React chart component.

2. In `Treemap` transform props
(`superset-frontend/plugins/plugin-chart-echarts/src/Treemap/transformProps.ts:170-187`),
leaf node names are derived via `formatSeriesName(...)`; when the underlying groupby
column holds the literal string `"null"`, `formatSeriesName` (see
`superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts:41-72`) returns
`"null"` unchanged because it only special-cases `name === null || name === undefined`.

3. During rendering, ECharts supplies `treePathInfo` to event handlers and
`extractTreePathInfo`
(`superset-frontend/plugins/plugin-chart-echarts/src/Treemap/constants.ts:27-36`) builds
`treePath` from `pathInfo.name`, so the click/contextmenu handlers in `EchartsTreemap`
receive `treePath` elements equal to `"null"` for those categories.

4. On context menu (right-click) in `EchartsTreemap` at
`superset-frontend/plugins/plugin-chart-echarts/src/Treemap/EchartsTreemap.tsx:117-155`,
the handler iterates `treePath.forEach((path, i) => { ... })` and executes `const val =
path === 'null' ? NULL_STRING() : path;` at line 127; for a real `"null"` category this
coerces `val` to `NULL_STRING()` (e.g. `<NULL>`), so the `drillToDetailFilters` and
`drillByFilters` built at lines 128-137 use `<NULL>` instead of `"null"` as the filter
value, causing drill-to-detail/drill-by requests to target the null token rather than the
actual `"null"` string rows.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/plugin-chart-echarts/src/Treemap/EchartsTreemap.tsx
**Line:** 127:127
**Comment:**
	*Incorrect Condition Logic: Mapping the literal string `'null'` to the null token changes semantics for real string values equal to `"null"`, so drill filters can target the wrong records. Preserve the original value unless you can reliably detect an actual null sentinel from the source data.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines 331 to 333
if (!value) {
value = NULL_STRING;
value = NULL_STRING();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This condition treats all falsy x-axis values as null, so legitimate values like 0 become <NULL>. That breaks category labeling and can produce wrong axis formatting/grouping. Restrict the null substitution to null/undefined (and handle empty string separately if needed). [falsy zero check]

Severity Level: Major ⚠️
- ⚠️ Waterfall x-axis mislabels 0 or empty categories.
- ⚠️ Waterfall chart groups falsy categories into null bucket.
Steps of Reproduction ✅
1. `MainPreset` registers `EchartsWaterfallChartPlugin` with key `VizType.Waterfall` in
`superset-frontend/src/visualizations/presets/MainPreset.ts:169-171`, and
`@superset-ui/plugin-chart-echarts` re-exports `WaterfallTransformProps` from
`superset-frontend/plugins/plugin-chart-echarts/src/index.ts:12` pointing to
`./Waterfall/transformProps`.

2. The unit test
`superset-frontend/plugins/plugin-chart-echarts/test/Waterfall/transformProps.test.ts:24-40`
shows how `transformProps` is invoked via `ChartProps` with `queriesData[0].data` and
formData specifying `x_axis` and `metric`; to reproduce the issue, use a similar setup but
include a row in `data` where the x-axis column (e.g. `year`) is a legitimate falsy value
like `0` or `''`.

3. When `transformProps` executes in
`superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts:159-231`,
it builds `transformedData` via `transformer` (lines 224-231) and then computes
`xAxisData` at lines 323-339 by mapping each `row` to an x-axis label; for each row it
sets `value = row[xAxisName]` or `row[breakdownName]` and then reaches the block at lines
331-333.

4. At line 331, `if (!value) { value = NULL_STRING(); }` treats any falsy `value` as null,
so a legitimate x-axis value of `0`, `false`, or `''` is converted to `NULL_STRING()`
instead of remaining `0`/`false`/`''`, and the resulting x-axis categories (used in
`echartOptions.xAxis.data` at line 446) and tooltip/title formatter `xAxisFormatter`
(lines 341-351) mislabel these categories as `<NULL>`, merging them into the null bucket
and breaking category labeling and grouping.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts
**Line:** 331:333
**Comment:**
	*Falsy Zero Check: This condition treats all falsy x-axis values as null, so legitimate values like `0` become `<NULL>`. That breaks category labeling and can produce wrong axis formatting/grouping. Restrict the null substitution to `null`/`undefined` (and handle empty string separately if needed).

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@bito-code-review

bito-code-review Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #8ceff1

Actionable Suggestions - 0
Additional Suggestions - 4
  • superset-frontend/src/components/FilterableTable/useCellContentParser.ts - 1
    • Inconsistent NULL_STRING value · Line 24-24
      The NULL_STRING value `'NULL'` is inconsistent with the canonical definition in `plugin-chart-echarts/src/constants.ts` which uses `t('')`. The `utils/common.ts` re-exports the canonical version, creating divergent behavior where the same constant renders differently depending on import source. Align to `t('')`.
      Code suggestion
      --- superset-frontend/src/components/FilterableTable/useCellContentParser.ts (line 24)
       24:-
       export const NULL_STRING = () => t('NULL');
      +
       export const NULL_STRING = () => t('<NULL>');
  • superset-frontend/plugins/plugin-chart-echarts/src/constants.ts - 1
    • Missing Python backend constants · Line 34-35
      New constants `TRUE_STRING` and `FALSE_STRING` added to TypeScript but not defined in Python backend (`constants.py`), breaking the cross-language sync pattern documented in the comment at line 30.
  • superset-frontend/src/utils/common.ts - 2
    • Complex type annotation · Line 45-45
      The type annotation `ReturnType` is unnecessarily complex. Since `NULL_STRING` is a function returning `string`, the return type should simply be `string`. This improves readability and maintainability.
    • Complex type annotation · Line 89-89
      Same type annotation complexity issue as line 45. Using `string` directly is clearer and consistent with TypeScript conventions.
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/constants.py - 1
    • Translation wrapping breaks comparison · Line 27-31
  • superset-frontend/plugins/plugin-chart-echarts/src/Treemap/EchartsTreemap.tsx - 1
  • superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts - 2
Review Details
  • Files reviewed - 18 · Commit Range: 16d83b1..16d83b1
    • superset-frontend/plugins/plugin-chart-echarts/src/Bubble/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Sunburst/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Treemap/EchartsTreemap.tsx
    • superset-frontend/plugins/plugin-chart-echarts/src/Waterfall/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/constants.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts
    • superset-frontend/src/components/FilterableTable/useCellContentParser.test.ts
    • superset-frontend/src/components/FilterableTable/useCellContentParser.ts
    • superset-frontend/src/components/FilterableTable/utils.tsx
    • superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx
    • superset-frontend/src/explore/exploreUtils/getSimpleSQLExpression.test.ts
    • superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx
    • superset-frontend/src/filters/utils.test.ts
    • superset-frontend/src/filters/utils.ts
    • superset-frontend/src/utils/common.test.tsx
    • superset-frontend/src/utils/common.ts
    • superset/constants.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

i18n:general Related to translations plugins size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant